#include "hedcontroller.h"
#include "euleroperator.h"
#include "vertex.h"
#include "edge.h"
#include "face.h"
#include "halfedge.h"  // they should not be here
#include <iostream>

#include "mvfs.h"
#include "mev.h"
#include "mef.h"
#include "mvse.h"
#include "mvr.h"
#include "mekr.h"


HEDController::HEDController()
{
	//eulerop.setModel(....);
}

void 
HEDController::doAddPoint(double x, double y, double z, double _tol)
{
  // always when adding a vertex into the topological data structure,
  // performs the following geometric checks:
  // 1: checks if the model already contains a vertex
  // 2: checks if the coordinate is the same of an existing vertex
  // 3: checks if the coordinate lies on an curve or on an region
  if( geomChecker.hasAnyVertex(model) ) // substitute it with cheaper test
  {
    int vId = 0;
    if( geomChecker.hasVertexWithGivenCoords(model,vId,x,y,z,_tol) )
    {
      std::cout << ".WARNING: There is already a vertex with the same coordinate! Id = " << vId << std::endl;
    }
    else
    {
      // finds the edge which the vertex lies, if it really happens!
      if( geomChecker.liesOnCurve(model,vId,x,y,z,_tol) )
      {
        Edge* edg = model.getEdge(x,y,z,_tol);
        int numVtx = model.getNumberOfVertexes();
        EulerOperator* mvse = new MVSE(edg, numVtx+1, x, y, z);
        mvse->execute();
        //eulerop.semv(edg, numVtx+1, x, y, z);
        m_undoredo.insertCommand( mvse );
      }
      else
      {
        // finds the face which the vertex lies, if it really happens!
        Face* fct = model.getFace(x,y,z,_tol);
        if( fct != NULL )
        {
          std::cout << ".FACE Id = " << fct->getFaceno() << std::endl;
          int numVtx = model.getNumberOfVertexes();
          EulerOperator* mvr = new MVR(fct, numVtx+1, x, y, z);
          mvr->execute();
          m_undoredo.insertCommand( mvr );
        }
        else
          std::cout << ".ERROR: It did not find a face! " << std::endl;
      }
    }
  }
  else
  {
    EulerOperator* mvfs = new MVFS(model,1, 1, 1, x, y, z);
    mvfs->execute();
    //eulerop.mvfs(model,1, 1, 1, x, y, z);
    m_undoredo.insertCommand( mvfs );
  }
}


void 
HEDController::doAddCurve(double x1, double y1, double z1, double x2, double y2, double z2, double _tol)
{
  // always when adding a curve into the topological data structure,
  // performs the following geometric checks:
  // 1: checks if the model already contains a vertex
  // 1: checks if the curve intersects a vertex of the model
  // 2: checks if the curve intersects another curve of the model
  // 3: checks if one end point of the curve belong to vertex of the model
  // 4: checks if both end points of the curve belong to vertexes of the model
  if( geomChecker.hasAnyVertex(model) ) // substitute it with cheaper test
  {
    int n = 0;
    list<Point> intPts;
    list<Edge*> iEdges;
    Point cp1(x1,y1,z1);
    Point cp2(x2,y2,z2);
    //geomChecker.intersectsVertex(model,curvePts,n,interPts);
    geomChecker.intersectsCurves(model,cp1,cp2,intPts,iEdges);
    std::cout << "-------------------- " << std::endl;
    std::cout << ".Testing intersections " << std::endl;
    std::cout << "...INTERSECTIONS = " << intPts.size() << std::endl;

    int v1Id = 0;
    int v2Id = 0;
    bool hasP1 = geomChecker.hasVertexWithGivenCoords(model,v1Id,x1,y1,z1,_tol);
    bool hasP2 = geomChecker.hasVertexWithGivenCoords(model,v2Id,x2,y2,z2,_tol);

    // removes from the candidates the points that can be attracted to the curve end points
    list<Edge*>::iterator itEd = iEdges.begin();
    list<Point>::iterator itPt = intPts.begin();
    for(; itPt!=intPts.end(); )
    {
      int vId = 0;
      double xi = itPt->getX();
      double yi = itPt->getY();  
      double zi = 0.0;  
      bool hasP = geomChecker.hasVertexWithGivenCoords(model,vId,xi,yi,zi,_tol);
      if( hasP && (hasP1 && (v1Id == vId)) || (hasP2 && (v2Id == vId)) )
      {
        itPt = intPts.erase(itPt);
        itEd = iEdges.erase(itEd);
      }
      else
      {
        itPt++;
        itEd++;
      }
    }
    std::cout << "...INTERSECTIONS = " << intPts.size() << std::endl;

    int numVtx = model.getNumberOfVertexes();
    int numFcs = model.getNumberOfFaces();
    if( hasP1 && hasP2 )
    {
      double v2Idi = numVtx+1;
      for(itEd=iEdges.begin(),itPt=intPts.begin(); itPt!=intPts.end(); itPt++,itEd++)
      {
        Edge* edg = *itEd;
        EulerOperator* mvse = new MVSE(edg, ++numVtx, itPt->getX(), itPt->getY(), 0.0);
        mvse->execute();
        m_undoredo.insertCommand( mvse );

        Vertex* vtx1 = model.getVertex(v1Id);
        Vertex* vtx2 = model.getVertex(v2Idi);
        HalfEdge* h1 = model.getHalfEdgeNEW(vtx1,vtx2->getPoint()->getX(),vtx2->getPoint()->getY(),vtx2->getPoint()->getZ());
        HalfEdge* h2 = model.getHalfEdgeNEW(vtx2,vtx1->getPoint()->getX(),vtx1->getPoint()->getY(),vtx1->getPoint()->getZ());
        // checks if the half-edges have the same loop to decide between MEF and MEKR
        if( h1->getLoop() != h2->getLoop() )
        {
          EulerOperator* mekr = new MEKR( h1, h2 );
          mekr->execute();
          m_undoredo.insertCommand( mekr );
        }
        else
        {
          EulerOperator* mef = new MEF( model, 1, v1Id, v2Idi, ++numFcs );
          mef->execute();
          m_undoredo.insertCommand( mef );
        }
        v1Id = numVtx;
        v2Idi = numVtx+1;
      }
      Vertex* vtx1 = model.getVertex(v1Id);
      Vertex* vtx2 = model.getVertex(v2Id);
      HalfEdge* h1 = model.getHalfEdgeNEW(vtx1,vtx2->getPoint()->getX(),vtx2->getPoint()->getY(),vtx2->getPoint()->getZ());
      HalfEdge* h2 = model.getHalfEdgeNEW(vtx2,vtx1->getPoint()->getX(),vtx1->getPoint()->getY(),vtx1->getPoint()->getZ());
      // checks if the half-edges have the same loop to decide between MEF and MEKR
      if( h1->getLoop() != h2->getLoop() )
      {
        EulerOperator* mekr = new MEKR( h1, h2 );
        mekr->execute();
        m_undoredo.insertCommand( mekr );
      }
      else
      {
        EulerOperator* mef = new MEF( model, 1, v1Id, v2Id, numFcs+1 );
        mef->execute();
        //eulerop.mef(model, 1, nFct, nVtx1, nVtx2, nVtx3, nVtx4, numFcs+1);
        //eulerop.smef(model, 1, v1Id, v2Id, numFcs+1);
        m_undoredo.insertCommand( mef );
      }
    }
    else if( hasP1 || hasP2 )
    {
      // supposing that there are intersections
      if( hasP2 )
      {
        Face* fct1 = model.getFace(x1,y1,z1,_tol);
        EulerOperator* mvr = new MVR( fct1, ++numVtx, x1, y1, z1 );
        mvr->execute();
        m_undoredo.insertCommand( mvr );
        v1Id = numVtx;
      }
      double v2Idi = numVtx+1;
      for(itEd=iEdges.begin(),itPt=intPts.begin(); itPt!=intPts.end(); itPt++,itEd++)
      {
        Edge* edg = *itEd;
        EulerOperator* mvse = new MVSE(edg, ++numVtx, itPt->getX(), itPt->getY(), 0.0);
        mvse->execute();
        m_undoredo.insertCommand( mvse );

        Vertex* vtx1 = model.getVertex(v1Id);
        Vertex* vtx2 = model.getVertex(v2Idi);
        HalfEdge* h1 = model.getHalfEdgeNEW(vtx1,vtx2->getPoint()->getX(),vtx2->getPoint()->getY(),vtx2->getPoint()->getZ());
        HalfEdge* h2 = model.getHalfEdgeNEW(vtx2,vtx1->getPoint()->getX(),vtx1->getPoint()->getY(),vtx1->getPoint()->getZ());
        // checks if the half-edges have the same loop to decide between MEF and MEKR
        if( h1->getLoop() != h2->getLoop() )
        {
          EulerOperator* mekr = new MEKR( h1, h2 );
          mekr->execute();
          m_undoredo.insertCommand( mekr );
        }
        else
        {
          EulerOperator* mef = new MEF( model, 1, v1Id, v2Idi, ++numFcs );
          mef->execute();
          m_undoredo.insertCommand( mef );
        }
        v1Id = numVtx;
        v2Idi = numVtx+1;
      }
      if( hasP1 )
      {
        EulerOperator* mev = new MEV( model, 1, v1Id, numVtx+1, x2, y2, z2 );
        mev->execute();
        m_undoredo.insertCommand( mev );
      }
      if( hasP2 )
      {
        Vertex* vtx1 = model.getVertex(v1Id);
        Vertex* vtx2 = model.getVertex(v2Id);
        HalfEdge* h1 = model.getHalfEdgeNEW(vtx1,vtx2->getPoint()->getX(),vtx2->getPoint()->getY(),vtx2->getPoint()->getZ());
        HalfEdge* h2 = model.getHalfEdgeNEW(vtx2,vtx1->getPoint()->getX(),vtx1->getPoint()->getY(),vtx1->getPoint()->getZ());
        // checks if the half-edges have the same loop to decide between MEF and MEKR
        if( h1->getLoop() != h2->getLoop() )
        {
          EulerOperator* mekr = new MEKR( h1, h2 );
          mekr->execute();
          m_undoredo.insertCommand( mekr );
        }
        else
        {
          EulerOperator* mef = new MEF( model, 1, v1Id, v2Id, ++numFcs );
          mef->execute();
          m_undoredo.insertCommand( mef );
        }
      }
    }
    else
    {
      // checks if the curve starts from a point in one edge
      int vId = 0;
      if( geomChecker.liesOnCurve(model,vId,x1,y1,z1,_tol)  )
      {
        Edge* edg1 = model.getEdge(x1,y1,z1,_tol);
        // removes from the list if it coincides
        if( intPts.size() > 0 ) 
        {
          itEd = iEdges.begin();
          itPt = intPts.begin();
          if( edg1 == *(itEd) )
          {
            intPts.erase(itPt);
            iEdges.erase(itEd);
          }
        }
        EulerOperator* mvse = new MVSE(edg1, ++numVtx, x1, y1, z1);
        mvse->execute();
        m_undoredo.insertCommand( mvse );
      }
      else
      {
        // finds the face which the point lies, if it really happens!
        Face* fct1 = model.getFace(x1,y1,z1,_tol);
        EulerOperator* mvr = new MVR( fct1, ++numVtx, x1, y1, z1 );
        mvr->execute();
        m_undoredo.insertCommand( mvr );
      }

      // checks if the curve ends in a point in one edge
      bool doTheLastMEV = true;
      if( geomChecker.liesOnCurve(model,vId,x2,y2,z2,_tol)  )
      {
        Edge* edg2 = model.getEdge(x2,y2,z2,_tol);
        // adds to the list if it does not coincide
        if( intPts.size() > 0 ) 
        {
          itEd = iEdges.end();
          itPt = intPts.end();
          if( edg2 == *(--itEd) )
          {
            intPts.erase(--itPt);
            iEdges.erase(itEd);
          }
        }
        intPts.push_back(Point(x2,y2,z2));
        iEdges.push_back(edg2);
        doTheLastMEV = false;
      }

      v1Id = numVtx;
      v2Id = numVtx+1;
      // supposing that there are intersections
      for(itEd=iEdges.begin(),itPt=intPts.begin(); itPt!=intPts.end(); itPt++,itEd++)
      {
        Edge* edg = *itEd;
        EulerOperator* mvse = new MVSE(edg, ++numVtx, itPt->getX(), itPt->getY(), 0.0);
        mvse->execute();
        m_undoredo.insertCommand( mvse );

        Vertex* vtx1 = model.getVertex(v1Id);
        Vertex* vtx2 = model.getVertex(v2Id);
        HalfEdge* h1 = model.getHalfEdgeNEW(vtx1,vtx2->getPoint()->getX(),vtx2->getPoint()->getY(),vtx2->getPoint()->getZ());
        HalfEdge* h2 = model.getHalfEdgeNEW(vtx2,vtx1->getPoint()->getX(),vtx1->getPoint()->getY(),vtx1->getPoint()->getZ());
        // checks if the half-edges have the same loop to decide between MEF and MEKR
        if( h1->getLoop() != h2->getLoop() )
        {
          EulerOperator* mekr = new MEKR( h1, h2 );
          mekr->execute();
          m_undoredo.insertCommand( mekr );
        }
        else
        {
          EulerOperator* mef = new MEF( model, 1, v1Id, v2Id, ++numFcs );
          mef->execute();
          m_undoredo.insertCommand( mef );
        }
        v1Id = numVtx;
        v2Id = numVtx+1;
      }

      if( doTheLastMEV )
      {
        EulerOperator* mev = new MEV( model, 1, v1Id, numVtx+1, x2, y2, z2 );
        mev->execute();
        m_undoredo.insertCommand( mev );
      }
    }
  }
  else
  {
    EulerOperator* mvfs = new MVFS( model, 1, 1, 1, x1, y1, z1 );
    mvfs->execute();
    EulerOperator* mev = new MEV( model, 1, 1, 2, x2, y2, z2 );
    mev->execute();
    //eulerop.mvfs(model,1, 1, 1, x1, y1, z1);
    //eulerop.mev(model, 1, 1, 1, 1, 1, 1, 2, x2, y2, z2);
    m_undoredo.insertCommand( mvfs );
    m_undoredo.insertCommand( mev );
  }
}


void HEDController::doAddTriangle(double x1, double y1, double z1, double x2, double y2, double z2,double x3, double y3, double z3)
{
  EulerOperator* mvfs = new MVFS(model,1, 1, 1, x1, y1, z1);
  mvfs->execute();
  EulerOperator* mev1 = new MEV(model, 1, 1, 1, 1, 1, 1, 2, x2, y2, z2);
  mev1->execute();
  EulerOperator* mev2 = new MEV(model, 1, 1, 1, 2, 1, 1, 3, x3, y3, z3);
  mev2->execute();
  EulerOperator* mef = new MEF( model, 1, 1, 1, 1, 1, 1, 2 );
  mef->execute();
//	eulerop.mvfs(model,1, 1, 1, x1, y1, z1);
//    eulerop.mev(model, 1, 1, 1, 1, 1, 1, 2, x2, y2, z2);
//    eulerop.mev(model, 1, 1, 1, 2, 1, 1, 3, x3, y3, z3);
//	eulerop.mef(model, 1, 1, 1, 1, 1, 1, 2);
  m_undoredo.insertCommand( mvfs );
  m_undoredo.insertCommand( mev1 );
  m_undoredo.insertCommand( mev2 );
  m_undoredo.insertCommand( mef );
}


void 
HEDController::doSelection(double x, double y, double z, double _tol)
{
  Vertex* v = model.getVertex(x,y,z,_tol);
  if(v != NULL)
  {
    v->setSelection(true);
  }
  else
  {
    Edge* e = model.getEdge(x,y,z,_tol);
    if(e != NULL)
    {
      e->setSelection(true);
    }
    else
    {
      Face* f = model.getFace(x,y,z,_tol);
      if( f != NULL )
        if( f->getLoopOut() != NULL ) // do not select the external face
          f->setSelection(true);
    }
  }
}


void 
HEDController::unselect()
{
  vector<Vertex*> vertices = model.getVertexes();
  for( size_t i=0; i< vertices.size(); i++ )
  {
    vertices[i]->setSelection(false);
  }
  vector<Edge*> edges = model.getEdges();
  for( size_t i=0; i< edges.size(); i++ )
  {
    edges[i]->setSelection(false);
  }
  vector<Face*> faces = model.getFaces();
  for( size_t i=0; i< faces.size(); i++ )
  {
    faces[i]->setSelection(false);
  }
}


void HEDController::doManipulate()
{
	std::cout << "HEDController::doManipulate nao implementado.\n";
}
void HEDController::doCreate()
{
	std::cout << "HEDController::doCreate nao implementado.\n";
}
void HEDController::doEdit()
{
	std::cout << "HEDController::doEdit nao implementado.\n";
}
void HEDController::doDelete()
{
	std::cout << "HEDController::doDelete nao implementado.\n";
}
void HEDController::doSave()
{
	std::cout << "HEDController::doSave nao implementado.\n";
}
void HEDController::doManage()
{
	std::cout << "HEDController::doManage nao implementado.\n";
}
void HEDController::doExit()
{
	std::cout << "HEDController::doExit nao implementado.\n";
}


HEDModel HEDController::getModel()
{
  return model;
}
